﻿using AutoMapper;
using Microsoft.Extensions.Configuration;
using Microsoft.IdentityModel.Tokens;
using System;
using System.Collections.Generic;
using System.IdentityModel.Tokens.Jwt;
using System.Linq;
using System.Linq.Expressions;
using System.Security.Claims;
using System.Text;
using System.Threading.Tasks;
using X23.Application.HttpManagement;
using X23.Application.RoleManagement.Dto;
using X23.Application.UserManagement.Dto;
using X23.EFCore.Repository;
using X23.EFCore.Service;
using X23.Model.Const;
using X23.Model.Role;
using X23.Model.User;
using X23.Util.SerializationUtil;

namespace X23.Application.UserManagement
{
    public class UserManagementService : CRUDService<UserDto, User>, IUserManagementService
    {
        private readonly IRepository<User> _userRepository;
        private readonly IMapper _mapper;
        private readonly IConfiguration _configuration;
        private readonly IWeChatHttpService _weChatHttpService;
        private readonly IRepository<UserRole> _userRoleRepository;
        private readonly IRepository<Role> _roleRepository;

        public UserManagementService(IRepository<User> userRepository, IRepository<Role> roleRepository, IRepository<UserRole> userRoleRepository, IMapper mapper, IConfiguration configuration, IWeChatHttpService weChatHttpService) : base(userRepository, mapper)
        {
            _userRepository = userRepository;
            _mapper = mapper;
            _configuration = configuration;
            _weChatHttpService = weChatHttpService;
            _userRoleRepository = userRoleRepository;
            _roleRepository = roleRepository;
        }

        /// <summary>
        /// 微信登陆
        /// </summary>
        /// <param name="code"></param>
        /// <returns></returns>
        /// <exception cref="Exception"></exception>
        public async Task<UserInfoDto> LoginByWeChat(string code)
        {
            var codeRes = await _weChatHttpService.JsCode2Session(code);
            var openId = codeRes.openid;
            if (string.IsNullOrEmpty(openId))
            {
                throw new Exception($"errcode:{codeRes.errcode},errmsg:{codeRes.errmsg}");
            }
            var user = (await _userRepository.GetAllAsync()).Where(x => x.WeChatId == openId).FirstOrDefault();
            if (user != null)
            {
                return await GetTokenByUserId(user.Id);
            }
            else
            {
                throw new Exception("账号密码错误！");
            }
        }


        /// <summary>
        /// 登录
        /// </summary>
        /// <param name="userDto"></param>
        /// <returns></returns>
        public async Task<UserInfoDto> Login(LoginUserDto userDto)
        {
            if (string.IsNullOrEmpty(userDto.Account) || string.IsNullOrEmpty(userDto.Password))
            {
                throw new Exception("账号或密码为空！");
            }
            var user = (await _userRepository.GetAllAsync()).Where(x => x.Account == userDto.Account && x.Password == userDto.Password).FirstOrDefault();
            if (user != null)
            {
                return await GetTokenByUserId(user.Id);
            }
            else
            {
                throw new Exception("账号密码错误！");
            }
        }

        /// <summary>
        /// 根据用户id获取token
        /// </summary>
        /// <param name="userId"></param>
        /// <returns></returns>
        public async Task<UserInfoDto> GetTokenByUserId(int userId)
        {
            var user = await _userRepository.GetByIdAsync(userId);
            //var expiresTime = DateTime.Now.AddHours(5);
            var expiresTime = X23.Util.DateTimeUtil.DateTimeUtil.DateTimeChineseNow.AddHours(5);

            var claims = new[]
            {
                new Claim(ClaimTypes.Name, user.Name),
                new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()),//SignalR中鉴权的用户信息
            };
            var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_configuration[AppSettings.JwtSymmetricSecurityKey]));
            var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
            var jwt = new JwtSecurityToken(
                issuer: _configuration[AppSettings.JwtIssuer],
                audience: _configuration[AppSettings.JwtAudience],
                claims: claims,
                expires: expiresTime,
                signingCredentials: creds);
            var token = new JwtSecurityTokenHandler().WriteToken(jwt);

            var resut = _mapper.Map<UserInfoDto>(user);
            resut.Token = token;
            resut.ExpiresTime = expiresTime;
            return resut;
        }
        /// <summary>
        /// 测试
        /// </summary>
        /// <param name="userId"></param>
        /// <returns></returns>
        public async Task<string> Test(int userId)
        {
            return (await _userRepository.GetByIdAsync(userId)).ToJson();
        }

        public async Task<bool> AddOrUpdateUser(UserDto userDto)
        {
            var query = await _userRepository.GetAllAsync();
            if (query.Where(x => x.Account == userDto.Account && x.Id != userDto.Id).Any())
            {
                throw new Exception("该账号已存在！");
            }
            var roleList = userDto.RoleIds;
            if (roleList == null || roleList.Count == 0)
            {
                throw new Exception("未选择角色！");
            }
            if (userDto.Id == 0)
            {
                var user = _mapper.Map<User>(userDto);
                user.Password = "666666";
                await _userRepository.AddAsync(user);

                var userRole = roleList.Select(x => new UserRole { UserId = user.Id, RoleId = x }).ToList();
                await _userRoleRepository.AddListAsync(userRole);
            }
            else
            {
                var user = await _userRepository.GetByIdAsync(userDto.Id);
                if (user.Account == "admin")
                {
                    throw new Exception("不可需改管理员账号！");
                }
                _mapper.Map(userDto, user);
                await _userRepository.UpdateAsync(user);

                var oldUserRole = (await _userRoleRepository.GetAllAsync()).Where(x => x.UserId == user.Id).ToList();

                var delUserRoleIds = oldUserRole.Where(x => !roleList.Contains(x.RoleId)).Select(x => x.Id).ToList();
                var addUserRole = roleList.Where(x => !oldUserRole.Select(x => x.RoleId).Contains(x))
                    .Select(x => new UserRole { UserId = user.Id, RoleId = x }).ToList();
                await _userRoleRepository.AddListAsync(addUserRole);
                await _userRoleRepository.DeleteListAsync(x => delUserRoleIds.Contains(x.Id));
            }


            return true;
        }

        public async Task<bool> UpdatePassword(int userId, string oldPassword, string newPassword)
        {
            var user = await _userRepository.GetByIdAsync(userId);
            if (user.Password == oldPassword && !string.IsNullOrWhiteSpace(newPassword))
            {
                user.Password = newPassword;
                return await _userRepository.UpdateAsync(user);
            }
            throw new Exception(string.IsNullOrWhiteSpace(newPassword) ? "新密码为空！" : "密码错误！");
        }

        public async Task<bool> DeleteUserAndUserRole(int userId)
        {
            await _userRepository.DeleteAsync(userId);
            var delUserRoleIds = (await _userRoleRepository.GetAllAsync()).Where(x => x.UserId == userId).Select(x => x.Id).ToList();
            await _userRoleRepository.DeleteListAsync(x => delUserRoleIds.Contains(x.Id));
            return true;
        }

        public async Task<List<UserRoleDto>> GetUserRoleList(Expression<Func<UserRole, bool>> predicate)
        {
            var query = from userRole in (await _userRoleRepository.GetAllAsync()).Where(predicate)
                        join role in (await _roleRepository.GetAllAsync())
                        on userRole.RoleId equals role.Id
                        select new UserRoleDto
                        {
                            Id = userRole.Id,
                            RoleId = userRole.RoleId,
                            RoleName = role.Name,
                            UserId = userRole.UserId
                        };
            return query.ToList();
        }
    }
}
